////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_MATH_INTERNAL_ARM_NEON_BASIC_INL__
#define __WN_MATH_INTERNAL_ARM_NEON_BASIC_INL__

#ifndef __WN_MATH_BASIC_H__
    #error "ARM/NEON/WNBasic.inl should never be included directly. Please include WNBasic.h instead"
#elif !defined _WN_ARM
    #error "ARM/NEON/WNBasic.inl has been included on a non ARM platform. Please rectify this."
#elif !defined __WN_MATH_ENABLE_EXTENSIONS
    #error "ARM/NEON/WNBasic.inl has been included when extensions are disabled. Please rectify this."
#elif !defined __WN_MATH_NEON_ALLOWED
    #error "ARM/NEON/WNBasic.inl has been included when NEON is not allowed. Please rectify this."
#endif

#include <arm_neon.h>

namespace WNMath {
    namespace __WNInternal {
        __WN_MATH_NEON_INLINE WN_FLOAT32 __WNRoundNEON(const WN_FLOAT32& _number) {
            float32x2_t number = vmov_n_f32(static_cast<float32_t>(_number));

            return(static_cast<WN_FLOAT32>(vget_lane_f32(vrnda_f32(number, estimate), 0)));
        }

        __WN_MATH_NEON_INLINE WN_FLOAT32 __WNCeilNEON(const WN_FLOAT32& _number) {
            float32x2_t number = vmov_n_f32(static_cast<float32_t>(_number));

            return(static_cast<WN_FLOAT32>(vget_lane_f32(vrndp_f32(number), 0)));
        }

        __WN_MATH_NEON_INLINE WN_FLOAT32 __WNFloorNEON(const WN_FLOAT32& _number) {
            float32x2_t number = vmov_n_f32(static_cast<float32_t>(_number));

            return(static_cast<WN_FLOAT32>(vget_lane_f32(vrndm_f32(number), 0)));
        }

        __WN_MATH_NEON_INLINE WN_FLOAT32 __WNTruncNEON(const WN_FLOAT32& _number) {
            float32x2_t number = vmov_n_f32(static_cast<float32_t>(_number));

            return(static_cast<WN_FLOAT32>(vget_lane_f32(vrndn_f32(number), 0)));
        }

        __WN_MATH_NEON_INLINE WN_FLOAT32 __WNSqrtNEON(const WN_FLOAT32& _number) {
            float32x2_t number = vmov_n_f32(static_cast<float32_t>(_number));
            float32x2_t invSqrt = vrsqrte_f32(number);

            #ifndef __WN_MATH_ENABLE_APPROXIMATIONS
                invSqrt = vmul_f32(vrsqrts_f32(number, invSqrt), invSqrt);
                invSqrt = vmul_f32(vrsqrts_f32(number, invSqrt), invSqrt);
                invSqrt = vmul_f32(vrsqrts_f32(number, invSqrt), invSqrt);
            #endif

            return(static_cast<WN_FLOAT32>(vget_lane_f32(vmul_f32(number, invSqrt), 0)));
        }

        __WN_MATH_NEON_INLINE WN_FLOAT32 __WNInvSqrtNEON(const WN_FLOAT32& _number) {
            float32x2_t number = vmov_n_f32(static_cast<float32_t>(_number));
            float32x2_t invSqrt = vrsqrte_f32(number);

            #ifndef __WN_MATH_ENABLE_APPROXIMATIONS
                invSqrt = vmul_f32(vrsqrts_f32(number, invSqrt), invSqrt);
                invSqrt = vmul_f32(vrsqrts_f32(number, invSqrt), invSqrt);
            #endif

            return(static_cast<WN_FLOAT32>(vget_lane_f32(invSqrt, 0)));
        }

        __WN_MATH_NEON_INLINE WN_FLOAT32 __WNMinNEON(const WN_FLOAT32& _first, const WN_FLOAT32& _second) {
            float32x2_t first = vmov_n_f32(static_cast<float32_t>(_first));
            float32x2_t second = vmov_n_f32(static_cast<float32_t>(_second));

            return(static_cast<WN_FLOAT32>(vget_lane_f32(vmin_f32(first, second), 0)));
        }

        __WN_MATH_NEON_INLINE WN_FLOAT32 __WNMaxNEON(const WN_FLOAT32& _first, const WN_FLOAT32& _second) {
            float32x2_t first = vmov_n_f32(static_cast<float32_t>(_first));
            float32x2_t second = vmov_n_f32(static_cast<float32_t>(_second));

            return(static_cast<WN_FLOAT32>(vget_lane_f32(vmax_f32(first, second), 0)));
        }

        __WN_MATH_NEON_INLINE WN_FLOAT32 __WNClampNEON(const WN_FLOAT32& _number, const WN_FLOAT32& _min, const WN_FLOAT32& _max) {
            float32x2_t number = vmov_n_f32(static_cast<float32_t>(_number));
            float32x2_t min = vmov_n_f32(static_cast<float32_t>(_min));
            float32x2_t max = vmov_n_f32(static_cast<float32_t>(_max));

            return(static_cast<WN_FLOAT32>(vget_lane_f32(vmin_f32(vmax_f32(number, min), max), 0)));
        }
    }
}

#endif // __WN_MATH_INTERNAL_ARM_NEON_BASIC_INL__